#include "QQOpenSSLApi.h"

void QQOpenSSLApi::encryptFileToSelf(const QString& filename, const QByteArray& key)
{
	qInfo() << "Starting encryption of file:" << filename << "to self.";

	EVP_CIPHER_CTX* cipherContext = EVP_CIPHER_CTX_new();
	if (!cipherContext) {
		qWarning() << "Failed to create EVP_CIPHER_CTX.";
		return;
	}
	qInfo() << "EVP_CIPHER_CTX created successfully.";

	unsigned char initializationVector[EVP_MAX_IV_LENGTH];
	if (!RAND_bytes(initializationVector, sizeof(initializationVector))) {
		qWarning() << "Failed to generate random IV.";
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Random IV generated.";

	if (EVP_EncryptInit_ex(cipherContext, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), initializationVector) != 1) {
		qWarning() << "EVP_EncryptInit_ex failed.";
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Encryption initialized with AES-256-CBC.";

	const int bufferSize = 1024;
	unsigned char inputBuffer[bufferSize];
	unsigned char encryptedBuffer[bufferSize + EVP_MAX_BLOCK_LENGTH];
	int bytesRead = 0;
	int bytesEncrypted = 0;

	QFile file(filename);
	if (!file.open(QIODevice::ReadWrite)) {
		qWarning() << "Failed to open file:" << filename;
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "File opened for encryption:" << filename;

	QByteArray encryptedData(reinterpret_cast<const char*>(initializationVector), AES_BLOCK_SIZE);
	while ((bytesRead = file.read(reinterpret_cast<char*>(inputBuffer), bufferSize)) > 0) {
		if (EVP_EncryptUpdate(cipherContext, encryptedBuffer, &bytesEncrypted, inputBuffer, bytesRead) != 1) {
			qWarning() << "EVP_EncryptUpdate failed.";
			file.close();
			EVP_CIPHER_CTX_free(cipherContext);
			return;
		}
		qInfo() << "Encrypted" << bytesRead << "bytes.";
		encryptedData.append(reinterpret_cast<char*>(encryptedBuffer), bytesEncrypted);
	}

	if (EVP_EncryptFinal_ex(cipherContext, encryptedBuffer, &bytesEncrypted) != 1) {
		qWarning() << "EVP_EncryptFinal_ex failed.";
		file.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Final encryption block processed.";

	encryptedData.append(reinterpret_cast<char*>(encryptedBuffer), bytesEncrypted);

	file.resize(0);
	if (file.write(encryptedData) == -1) {
		qWarning() << "Failed to write encrypted data to file:" << filename;
	}
	else {
		qInfo() << "Encrypted data written to file:" << filename;
	}

	file.close();
	EVP_CIPHER_CTX_free(cipherContext);
	qInfo() << "Encryption process completed.";
}

void QQOpenSSLApi::encryptFileToTarget(const QString& filename, const QString& target, const QByteArray& key)
{
	qInfo() << "Starting encryption of file:" << filename << "to target:" << target;

	EVP_CIPHER_CTX* cipherContext = EVP_CIPHER_CTX_new();
	if (!cipherContext) {
		qWarning() << "Failed to create EVP_CIPHER_CTX.";
		return;
	}
	qInfo() << "EVP_CIPHER_CTX created successfully.";

	unsigned char initializationVector[EVP_MAX_IV_LENGTH];
	if (!RAND_bytes(initializationVector, sizeof(initializationVector))) {
		qWarning() << "Failed to generate random IV.";
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Random IV generated.";

	if (EVP_EncryptInit_ex(cipherContext, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), initializationVector) != 1) {
		qWarning() << "EVP_EncryptInit_ex failed.";
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Encryption initialized with AES-256-CBC.";

	const int bufferSize = 1024;
	unsigned char inputBuffer[bufferSize];
	unsigned char encryptedBuffer[bufferSize + EVP_MAX_BLOCK_LENGTH];
	int bytesRead = 0;
	int bytesEncrypted = 0;

	QFile sourceFile(filename);
	if (!sourceFile.open(QIODevice::ReadOnly)) {
		qWarning() << "Failed to open source file for encryption:" << filename;
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Source file opened for encryption.";

	QByteArray encryptedData(reinterpret_cast<const char*>(initializationVector), AES_BLOCK_SIZE);
	while ((bytesRead = sourceFile.read(reinterpret_cast<char*>(inputBuffer), bufferSize)) > 0) {
		if (EVP_EncryptUpdate(cipherContext, encryptedBuffer, &bytesEncrypted, inputBuffer, bytesRead) != 1) {
			qWarning() << "EVP_EncryptUpdate failed.";
			sourceFile.close();
			EVP_CIPHER_CTX_free(cipherContext);
			return;
		}
		qInfo() << "Encrypted" << bytesRead << "bytes.";
		encryptedData.append(reinterpret_cast<char*>(encryptedBuffer), bytesEncrypted);
	}

	if (EVP_EncryptFinal_ex(cipherContext, encryptedBuffer, &bytesEncrypted) != 1) {
		qWarning() << "EVP_EncryptFinal_ex failed.";
		sourceFile.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Final encryption block processed.";

	encryptedData.append(reinterpret_cast<char*>(encryptedBuffer), bytesEncrypted);

	QFile destFile(target);
	if (!destFile.open(QIODevice::WriteOnly)) {
		qWarning() << "Failed to open target file for writing:" << target;
		sourceFile.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Target file opened for writing.";

	if (destFile.write(encryptedData) == -1) {
		qWarning() << "Failed to write encrypted data to target file:" << target;
	}
	else {
		qInfo() << "Encrypted data written to target file:" << target;
	}

	destFile.close();
	sourceFile.close();
	EVP_CIPHER_CTX_free(cipherContext);

	qInfo() << "Encryption process completed.";
}

void QQOpenSSLApi::decryptFileToSelf(const QString& filename, const QByteArray& key)
{
	qInfo() << "Starting decryption of file:" << filename << "to self.";

	EVP_CIPHER_CTX* cipherContext = EVP_CIPHER_CTX_new();
	if (!cipherContext) {
		qWarning() << "Failed to create EVP_CIPHER_CTX.";
		return;
	}

	QFile file(filename);
	if (!file.open(QIODevice::ReadWrite)) {
		qWarning() << "Failed to open file for decryption:" << filename;
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "File opened for decryption.";

	unsigned char initializationVector[EVP_MAX_IV_LENGTH];
	if (file.read(reinterpret_cast<char*>(initializationVector), EVP_MAX_IV_LENGTH) == -1) {
		qWarning() << "Failed to read IV from file:" << filename;
		file.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "IV read from file.";

	if (EVP_DecryptInit_ex(cipherContext, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), initializationVector) != 1) {
		qWarning() << "EVP_DecryptInit_ex failed.";
		file.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Decryption initialized with AES-256-CBC.";

	const int bufferSize = 1024;
	unsigned char inputBuffer[bufferSize];
	unsigned char decryptedBuffer[bufferSize + EVP_MAX_BLOCK_LENGTH];
	int bytesRead = 0;
	int bytesDecrypted = 0;

	QByteArray decryptedData;
	while ((bytesRead = file.read(reinterpret_cast<char*>(inputBuffer), bufferSize)) > 0) {
		if (EVP_DecryptUpdate(cipherContext, decryptedBuffer, &bytesDecrypted, inputBuffer, bytesRead) != 1) {
			qWarning() << "EVP_DecryptUpdate failed.";
			file.close();
			EVP_CIPHER_CTX_free(cipherContext);
			return;
		}
		qInfo() << "Decrypted" << bytesRead << "bytes.";
		decryptedData.append(reinterpret_cast<char*>(decryptedBuffer), bytesDecrypted);
	}

	if (EVP_DecryptFinal_ex(cipherContext, decryptedBuffer, &bytesDecrypted) != 1) {
		qWarning() << "EVP_DecryptFinal_ex failed.";
		file.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Final decryption block processed.";

	decryptedData.append(reinterpret_cast<char*>(decryptedBuffer), bytesDecrypted);

	file.resize(0);
	if (file.write(decryptedData) == -1) {
		qWarning() << "Failed to write decrypted data to file:" << filename;
	}
	else {
		qInfo() << "Decrypted data written to file:" << filename;
	}

	file.close();
	EVP_CIPHER_CTX_free(cipherContext);

	qInfo() << "Decryption process completed.";
}

void QQOpenSSLApi::decryptFileToTarget(const QString& filename, const QString& target, const QByteArray& key)
{
	qInfo() << "Starting decryption of file:" << filename << "to target:" << target;

	EVP_CIPHER_CTX* cipherContext = EVP_CIPHER_CTX_new();
	if (!cipherContext) {
		qWarning() << "Failed to create EVP_CIPHER_CTX.";
		return;
	}

	QFile sourceFile(filename);
	if (!sourceFile.open(QIODevice::ReadOnly)) {
		qWarning() << "Failed to open file for decryption:" << filename;
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "File opened for decryption.";

	unsigned char initializationVector[EVP_MAX_IV_LENGTH];
	if (sourceFile.read(reinterpret_cast<char*>(initializationVector), EVP_MAX_IV_LENGTH) == -1) {
		qWarning() << "Failed to read IV from file:" << filename;
		sourceFile.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "IV read from file.";

	if (EVP_DecryptInit_ex(cipherContext, EVP_aes_256_cbc(), nullptr, reinterpret_cast<const unsigned char*>(key.data()), initializationVector) != 1) {
		qWarning() << "EVP_DecryptInit_ex failed.";
		sourceFile.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Decryption initialized with AES-256-CBC.";

	const int bufferSize = 1024;
	unsigned char inputBuffer[bufferSize];
	unsigned char decryptedBuffer[bufferSize + EVP_MAX_BLOCK_LENGTH];
	int bytesRead = 0;
	int bytesDecrypted = 0;

	QByteArray decryptedData;
	while ((bytesRead = sourceFile.read(reinterpret_cast<char*>(inputBuffer), bufferSize)) > 0) {
		if (EVP_DecryptUpdate(cipherContext, decryptedBuffer, &bytesDecrypted, inputBuffer, bytesRead) != 1) {
			qWarning() << "EVP_DecryptUpdate failed.";
			sourceFile.close();
			EVP_CIPHER_CTX_free(cipherContext);
			return;
		}
		qInfo() << "Decrypted" << bytesRead << "bytes.";
		decryptedData.append(reinterpret_cast<char*>(decryptedBuffer), bytesDecrypted);
	}

	if (EVP_DecryptFinal_ex(cipherContext, decryptedBuffer, &bytesDecrypted) != 1) {
		qWarning() << "EVP_DecryptFinal_ex failed.";
		sourceFile.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Final decryption block processed.";

	decryptedData.append(reinterpret_cast<char*>(decryptedBuffer), bytesDecrypted);

	QFile destFile(target);
	if (!destFile.open(QIODevice::WriteOnly)) {
		qWarning() << "Failed to open target file for writing:" << target;
		sourceFile.close();
		EVP_CIPHER_CTX_free(cipherContext);
		return;
	}
	qInfo() << "Target file opened for writing.";

	if (destFile.write(decryptedData) == -1) {
		qWarning() << "Failed to write decrypted data to target file:" << target;
	}
	else {
		qInfo() << "Decrypted data written to target file:" << target;
	}

	destFile.close();
	sourceFile.close();
	EVP_CIPHER_CTX_free(cipherContext);

	qInfo() << "Decryption process completed.";
}